package com.hopes.bizz.hazard.alarm.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hopes.bizz.basic.common.api.feign.RemoteParkBasicDictService;
import com.hopes.bizz.basic.hazard.source.entity.HazMonitorFactorEntity;
import com.hopes.bizz.common.core.util.DateUtils;
import com.hopes.bizz.hazard.alarm.dto.HazAlarmOverproofExcelDTO;
import com.hopes.bizz.hazard.alarm.entity.HazAlarmOverproofEntity;
import com.hopes.bizz.hazard.alarm.mapper.HazAlarmOverproofMapper;
import com.hopes.bizz.hazard.alarm.service.HazAlarmOverproofService;
import com.hopes.bizz.hazard.alarm.vo.HazAlarmOverproofExcelVO;
import com.hopes.bizz.hazard.common.constant.HazardDicConstant;
import com.hopes.bizz.hazard.common.constant.RedisKeyConstant;
import com.hopes.bizz.hazard.om.entity.HazRtDataEntity;
import com.hopes.bizz.hazard.om.service.HazRtDataService;
import com.hopes.boss.common.core.constant.SecurityConstants;
import com.hopes.boss.common.data.resolver.ParamResolver;
import com.hopes.boss.common.entity.ValidateImportBO;
import com.hopes.boss.common.excel.vo.ErrorMessage;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 危险源超标报警
 *
 * @author daijie
 * @date 2023-11-16 19:26:17
 */
@Service
public class HazAlarmOverproofServiceImpl extends ServiceImpl<HazAlarmOverproofMapper, HazAlarmOverproofEntity> implements HazAlarmOverproofService {

    @Lazy
    @Resource
    private HazRtDataService hazRtDataService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RemoteParkBasicDictService remoteParkBasicDictService;

    @Override
    public ValidateImportBO<HazAlarmOverproofExcelDTO> validateImport(List<HazAlarmOverproofExcelDTO> excelDTOList) {
        ValidateImportBO<HazAlarmOverproofExcelDTO> bo = new ValidateImportBO<>();
        List<ErrorMessage> errorMessageList = new ArrayList<>();
        List<HazAlarmOverproofExcelDTO> saveList = new ArrayList<>(excelDTOList);
        int successNum = excelDTOList.size();
        int failedNum = 0;

        bo.setErrorMessages(errorMessageList);
        bo.setSaveList(saveList);
        bo.setSuccessNum(successNum);
        bo.setFailedNum(failedNum);
        return bo;
    }

    @Override
    public HazAlarmOverproofEntity importDtoToEntity(HazAlarmOverproofExcelDTO excelDto) {
        HazAlarmOverproofEntity entity = new HazAlarmOverproofEntity();
        BeanUtils.copyProperties(excelDto, entity);
        return entity;
    }

    @Override
    public HazAlarmOverproofExcelVO exportEntityToVo(HazAlarmOverproofEntity entity) {
        HazAlarmOverproofExcelVO excelVO = new HazAlarmOverproofExcelVO();
        BeanUtils.copyProperties(entity, excelVO);
        return excelVO;
    }

    @Override
    public Boolean alarmOverProofJobHandler() {
        // step.1 查询全部正在使用的站点
        final List<HazMonitorFactorEntity> monitorFactorList = remoteParkBasicDictService.getHazMonitorFactorList(SecurityConstants.FROM_IN).getData();

        final Date now = new Date();
        final Long alarmTimes = ParamResolver.getLong("HAZARD_ALARM_TIMES", 5L);
        for (HazMonitorFactorEntity monitorFactor : monitorFactorList) {
            // step.2 查询当前实时数据
            final HazRtDataEntity rtData = hazRtDataService.getOne(Wrappers.lambdaQuery(HazRtDataEntity.class).eq(HazRtDataEntity::getMonitorFactorId, monitorFactor.getId()));

            // 没有实时数据
            if (rtData == null) {
                continue;
            }

            // 没有设置低报和高报开关
            if (!monitorFactor.getHaveHighAlarmFlag() && !monitorFactor.getHaveLowAlarmFlag()) {
                clearAlarmNetIfExist(monitorFactor);
                continue;
            }
            BigDecimal val = rtData.getMonitorData();
            if (val == null) {
                clearAlarmNetIfExist(monitorFactor);
                continue;
            }
            BigDecimal valueH = monitorFactor.getValueH();
            BigDecimal valueHh = monitorFactor.getValueHh();
            BigDecimal valueL = monitorFactor.getValueL();
            BigDecimal valueLl = monitorFactor.getValueLl();

            String key = RedisKeyConstant.ALARM_DATE + monitorFactor.getId();
            if (!isAlarm(val, valueH, valueHh, valueL, valueLl)) {
                stringRedisTemplate.delete(key);
                clearAlarmNetIfExist(monitorFactor);
                continue;
            }
            final Boolean hasKey = Optional.ofNullable(stringRedisTemplate.hasKey(key)).orElse(Boolean.FALSE);
            if (!hasKey) {
                stringRedisTemplate.opsForValue().set(key, String.valueOf(rtData.getDataTime().getTime()));
            }
            final Long min = DateUtils.getIntervalMin(
                    DateUtils.getTimeStrByMilliSecond(Long.parseLong(Objects.requireNonNull(stringRedisTemplate.opsForValue().get(key)))), now);

            if (min < alarmTimes) {
                clearAlarmNetIfExist(monitorFactor);
                continue;
            }

            if (val.compareTo(valueHh) > 0) {
                saveOrUpdateAlarmNet(monitorFactor, now, min,
                        valueHh, val, getExcessMultiple(val, valueHh));
            } else if (val.compareTo(valueH) > 0) {
                saveOrUpdateAlarmNet(monitorFactor, now, min,
                        valueH, val, getExcessMultiple(val, valueH));
            } else if (val.compareTo(valueLl) < 0) {
                saveOrUpdateAlarmNet(monitorFactor, now, min,
                        valueLl, val, getExcessMultiple(val, valueLl));
            } else if (val.compareTo(valueL) < 0) {
                saveOrUpdateAlarmNet(monitorFactor, now, min,
                        valueL, val, getExcessMultiple(val, valueL));
            } else {
                clearAlarmNetIfExist(monitorFactor);
            }
        }
        return true;
    }

    private boolean isAlarm(BigDecimal val, BigDecimal valueH, BigDecimal valueHh, BigDecimal valueL, BigDecimal valueLl) {
        if (val.compareTo(valueHh) > 0) {
            return true;
        } else if (val.compareTo(valueH) > 0) {
            return true;
        } else if (val.compareTo(valueLl) < 0) {
            return true;
        } else {
            return val.compareTo(valueL) < 0;
        }
    }

    private void saveOrUpdateAlarmNet(HazMonitorFactorEntity monitorFactor, Date date, Long min,
                                      BigDecimal alarmConfigValue, BigDecimal monitorValue, BigDecimal excessMultiple) {
        // 是否已经有报警
        LambdaQueryWrapper<HazAlarmOverproofEntity> alarmWrapper = Wrappers.lambdaQuery();
        alarmWrapper.eq(HazAlarmOverproofEntity::getMonitorFactorId, monitorFactor.getId());
        alarmWrapper.eq(HazAlarmOverproofEntity::getAlarmStatus, HazardDicConstant.HAZ_ALARM_STATUS_ALARM);
        HazAlarmOverproofEntity exist = this.getOne(alarmWrapper, false);

        if (exist != null) {
            String handleStatus = exist.getHandleStatus();
            if (Objects.equals(handleStatus, HazardDicConstant.HAZ_ALARM_HANDLE_STATUS_TODO)) {
                // 有则更新持续时间和报警级别、监测倍数、监测值
                exist.setMonitorData(monitorValue);
                exist.setAlarmMultiple(excessMultiple);
                exist.setDurationTime(min);
                exist.setAlarmContent(buildAlarmContent(exist.getAlarmTimeStr(),
                        monitorFactor.getFactorName(), monitorValue, excessMultiple, alarmConfigValue));

                this.updateById(exist);
                updateMonitorFactorAlarmStatus(monitorFactor, true);
                return;
            }
            // 已经处理的就构造新的报警
        }
        // 新造报警
        HazAlarmOverproofEntity alarmEntity = new HazAlarmOverproofEntity();
        alarmEntity.setAlarmTime(date);
        alarmEntity.setDurationTime(min);
        alarmEntity.setMonitorData(monitorValue);
        alarmEntity.setAlarmValue(alarmConfigValue);
        alarmEntity.setOrgId(alarmEntity.getOrgId());
        alarmEntity.setAlarmMultiple(excessMultiple);
        alarmEntity.setMonitorFactorId(monitorFactor.getId());
        alarmEntity.setHazardSourceId(monitorFactor.getHazardSourceId());
        alarmEntity.setAlarmStatus(HazardDicConstant.HAZ_ALARM_STATUS_ALARM);
        alarmEntity.setHandleStatus(HazardDicConstant.HAZ_ALARM_HANDLE_STATUS_TODO);
        alarmEntity.setAlarmContent(buildAlarmContent(alarmEntity.getAlarmTimeStr(),
                monitorFactor.getFactorName(), monitorValue, excessMultiple, alarmConfigValue));
        this.save(alarmEntity);
        updateMonitorFactorAlarmStatus(monitorFactor, true);

//        MessageDO messageDO = new MessageDO();
//        messageDO.setNeedBroadcast(Boolean.TRUE);
//        messageDO.setTenantId(TenantContextHolder.getTenantId().toString());
//        messageDO.setMessageText(alarmEntity.getAlarmContent());
//        RedisMessageDistributor messageDistributor = SpringContextHolder.getBean(RedisMessageDistributor.class);
//        messageDistributor.distribute(messageDO);
    }

    /**
     * 清空报警
     * @param monitorFactor  监控因子
     */
    private void clearAlarmNetIfExist(HazMonitorFactorEntity monitorFactor) {
        // 是否已经有报警
        LambdaQueryWrapper<HazAlarmOverproofEntity> alarmOverProofWrapper = Wrappers.<HazAlarmOverproofEntity>lambdaQuery()
                .eq(HazAlarmOverproofEntity::getMonitorFactorId, monitorFactor.getId())
                .eq(HazAlarmOverproofEntity::getAlarmStatus, HazardDicConstant.HAZ_ALARM_STATUS_ALARM);
        final List<HazAlarmOverproofEntity> list = this.list(alarmOverProofWrapper);
        // 消除报警
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(item -> {
                item.setAlarmStatus(HazardDicConstant.HAZ_ALARM_STATUS_REMOVE);
                item.setRemoveTime(new Date());
            });
            this.updateBatchById(list);
        }
        // 更新监控因子报警状态
        updateMonitorFactorAlarmStatus(monitorFactor, false);
    }

    private void updateMonitorFactorAlarmStatus(HazMonitorFactorEntity monitorFactor, boolean flag) {
        monitorFactor.setAlarmOverStandard(flag);
        remoteParkBasicDictService.updateHazMonitorFactorById(monitorFactor, SecurityConstants.FROM_IN);
    }

    private BigDecimal getExcessMultiple(BigDecimal val, BigDecimal config) {
        if (val.compareTo(config) < 0) {
            return (config.subtract(val)).divide(config, 2, RoundingMode.UP);
        } else {
            return val.divide(config, 2, RoundingMode.UP);
        }
    }

    private String buildAlarmContent(String date, String monitorFactor, BigDecimal monitorValue, BigDecimal excessMultiple, BigDecimal alarmConfigValue) {
        // [超标报警]，开始时间[var]，点位[var]，报警因子[var]，监测值为[var]，超过低报报警阈值[var]，超标倍数[var], 特此通知!
        StringBuilder sb = new StringBuilder("[超标]");
        sb.append("开始时间:").append(date)
                .append(", 点位:").append(monitorFactor);
        sb.append(", ").append("监测值为:").append(monitorValue).append(", ");
        // 超标倍数大于1就是高过报警
        if (excessMultiple.compareTo(BigDecimal.ONE) > 0) {
            sb.append("超过高报报警阈值:").append(alarmConfigValue).append(", ");
        } else {
            sb.append("超过低报报警阈值:").append(alarmConfigValue).append(", ");
        }
        sb.append("超标倍数:").append(excessMultiple);
        sb.append(", 特此通知!");
        return sb.toString();
    }
}
